<?php


namespace app\common;

use app\common\service\export\ExportService;
use app\common\trait\BaseTrait;
use app\common\trait\LaravelTrait;
use app\common\trait\RewriteCollectionTrait;
use Closure;
use ReflectionClass;
use ReflectionMethod;
use think\{
    Collection,
    db\Query,
    db\Where,
    facade\Log,
    helper\Str,
    Model,
    model\contract\Modelable,
    model\relation\BelongsToMany,
    model\relation\HasMany
};
use think\db\exception\{DataNotFoundException, DbException, ModelNotFoundException};
use Throwable;
/**
 * 
 * @method mixed getTableFields()
 */ 
class BaseModel extends Model
{
    use BaseTrait;
    use LaravelTrait;
    use RewriteCollectionTrait;

    protected bool $autoWriteTimestamp = true;

    // 默认配置
    protected int $defaultPageSize = 15;
    protected int $maxResults = 1000;

    protected string $deleteTime="deleted_at";


    /**
     * 导出入口方法
     */
    public function export(array $headers, string $filename = 'export', string $type = 'xlsx', bool $forceQueue = false)
    {
        // 获取当前查询条件
        $query = $this->getCurrentQuery();
        $modelClass = get_class($this);

        // 估算数据量
        $exportService = new ExportService();
        $estimatedCount = $exportService->estimateCount(function () use ($query) {
            return clone $query;
        });

        // 判断是否需要走队列
        $threshold = config('export.large_data_threshold', 100000);
        if ($forceQueue || $estimatedCount > $threshold) {
            return $exportService->createQueueTask(
                $modelClass,
                $query,
                $headers,
                $filename,
                $type
            );
        }

        // 直接导出
        return $exportService->directExport(
            clone $query,
            $headers,
            $filename,
            $type
        );
    }

    /**
     * 获取当前查询对象
     */
    protected function getCurrentQuery(): BaseModel|static
    {
        return clone $this;
    }

    /**
     * 支持链式调用的导出方法
     */
    public function exportData(array $headers, string $filename = 'export', string $type = 'xlsx', bool $forceQueue = false)
    {
        return $this->export($headers, $filename, $type, $forceQueue);
    }


    /**
     * 估算查询结果数量（适合判断是否超大数据量）
     * @param Closure $queryCallback 查询构造回调
     * @return int 估算的行数
     */
    public function estimateCount(Closure $queryCallback): int
    {
        $query = $queryCallback();
        $sql = $query->fetchSql()->select(); // 获取查询SQL

        // 构造EXPLAIN语句
        $explainSql = "EXPLAIN " . $sql;
        $result = \think\facade\Db::query($explainSql);

        // 返回估算的行数（不同数据库字段可能不同，MySQL是rows）
        return $result[0]['rows'] ?? 0;
    }


    /**
     * 精确分段计数（适合需要准确判断阈值的场景）
     * @param Closure $queryCallback 查询构造回调
     * @param int $batchSize 每次查询的批次大小
     * @param int $threshold 阈值（超过此值则停止计数）
     * @return int 计数结果（不超过阈值时返回精确值，否则返回阈值+1）
     */
    public function batchCount(Closure $queryCallback, int $batchSize = 10000, int $threshold = 1000000): int
    {
        $query = $queryCallback();
        $count = 0;
        $offset = 0;

        do {
            // 每次查询一批数据的ID（仅查主键，减少IO）
            $ids = $query->limit($batchSize)->offset($offset)->column('id');
            $batchCount = count($ids);
            $count += $batchCount;

            // 如果已超过阈值，提前终止
            if ($count > $threshold) {
                return $threshold + 1;
            }

            $offset += $batchSize;
        } while ($batchCount == $batchSize); // 直到某批数据不足batchSize，说明已到末尾

        return $count;
    }



    public function getByKey($id)
    {

        $pk = $this->getPk();
        $string = "getBy" . Str::studly($pk);
        return self::$string($id);
    }

    public function columnToString(Collection|HasMany|BelongsToMany|Query $collection, $columnName, $sep = ",")
    {
        if ($collection instanceof Collection) {
            $array = array_column($collection->toArray(), $columnName);
            return implode($sep, $array);
        } else {
            return implode($sep, $collection->column($columnName));
        }
    }

    public function fetchData(mixed $conditions = [], array $config = []): \think\Collection|array
    {
        // 判断是否需要分页
        if (request()->has("page", "get") || request()->has("list_rows", "get") || isset($config['pageNum']) || isset($config['pageSize'])) {
            if (request()->has("page", "get", true)) {
                $config['pageNum'] = request()->get("page", 1);
            }
            if (request()->has("list_rows", "get", true)) {
                $config['pageSize'] = request()->get("list_rows", 15);
            }
            return $this->fetchPaginated($conditions, $config);
        }
        return $this->fetchAll($conditions, $config, true);
    }

    /**
     * 获取分页数据
     */
    public function fetchPaginated(
        $conditions = [],
        array $config = []
    ): array {
        // 创建新实例避免污染当前实例
        $query = new static();
        
        // 应用条件
        if (!empty($conditions)) {
            $query->where($conditions);
        }
        
        // 应用配置
        $this->applyConfig($query, $config);
        
        // 获取分页参数
        $page = $this->getPageParam($config);
        $pageSize = $this->getPageSizeParam($config);
        
        $total = $query->count();
        try {
            $list = $query->page($page, $pageSize)->select()->toArray();
        } catch (DataNotFoundException | ModelNotFoundException | DbException $e) {
            $list = [];
        }
        return ['total' => $total, 'list' => $list, 'current_page' => $page, 'per_page' => $pageSize];
    }

    /**
     * 获取所有数据
     */
    public function fetchAll(
        $conditions = [],
        array $config = [],
        bool $force = false
    ): Collection {
        // 创建新实例避免污染当前实例
        $query = new static();
        
        // 应用条件
        if (!empty($conditions)) {
            $query->where($conditions);
        }
        
        // 应用配置
        $this->applyConfig($query, $config);
        
        // 强制获取时不做限制
        if ($force) {
            return $query->select();
        }

        // 执行安全数量检查
        $total = $query->count();
        if ($total > $this->maxResults) {
            return $query->limit($this->maxResults)->select();
        }

        return $query->select();
    }



    /**
     * 获取分页参数
     */
    protected function getPageParam(array $config): int
    {
        $page = $config['pageNum'] ?? request()->param('page', 1);
        return is_numeric($page) ? (int)$page : 1;
    }

    /**
     * 获取分页大小参数
     */
    protected function getPageSizeParam(array $config): int
    {
        $size = $config['pageSize'] ?? request()->param('list_rows', $this->defaultPageSize);
        return is_numeric($size) ? (int)$size : $this->defaultPageSize;
    }

    /**********************
     * 实用助手方法
     *********************/

    /**
     * 创建条件关联
     */
    public function createRelationCondition(string $relation, $condition): array
    {
        return [
            $relation => function ($query) use ($condition) {
                if ($condition instanceof Closure) {
                    $condition($query);
                } else {
                    $query->where($condition);
                }
            }
        ];
    }

    /**
     * 创建字段关联
     */
    public function createRelationFields(string $relation, array $fields): array
    {
        return [
            $relation => function ($query) use ($fields) {
                $query->field($fields);
            }
        ];
    }

    /**
     * 创建连接参数
     */
    public static function createJoinParams(
        string $table,
        string $on,
        $fields = [],
        string $type = 'INNER',
        string $alias = ''
    ): array {
        return [
            'table' => $table,
            'on' => $on,
            'fields' => $fields,
            'type' => $type,
            'alias' => $alias
        ];
    }

    /**
     * 获取请求分页参数
     */
    public function getRequestPageParams(): array
    {
        return [
            'page' => $this->getPageParam([]),
            'pageSize' => $this->getPageSizeParam([])
        ];
    }


    /**
     * 获取单条数据（优化版本）
     */
    public function fetchOne($conditions, array $config = []): Model
    {
        // 创建新实例避免污染当前实例
        $query = new static();
        
        // 应用条件
        if (!empty($conditions)) {
            // 如果是主键查询，直接使用 find
            if (is_numeric($conditions) || (is_string($conditions) && !preg_match('/\s+/', $conditions))) {
                $result = $query->find($conditions);
                if ($result) {
                    // 应用配置到结果模型
                    $this->applyConfigToModel($result, $config);
                    return $result;
                }
                return $query->findOrEmpty();
            } else {
                // 其他条件使用 where
                $query->where($conditions);
            }
        }
        
        // 应用配置
        $this->applyConfig($query, $config);
        
        // 获取结果（找不到时返回空模型）
        return $query->findOrEmpty();
    }

    /**
     * 查询数据或创建（使用fetchOne优化）
     */
    public function fetchOneOrCreate($data): Model|Modelable
    {
        // 使用fetchOne替代直接查询
        $model = $this->fetchOne($data);

        // 如果找到则返回
        if (!$model->isEmpty()) {
            return $model;
        }

        // 否则创建新记录
        return $this->create($data);
    }


    /**
     * 智能创建数据（主模型+一对多关联）
     * @param array $data 创建数据
     * @return BaseModel|false 创建成功返回模型实例，失败返回false
     */
    public function intelligentCreate(array $data): BaseModel|false
    {
        // 开启事务（与更新方法保持一致的事务处理）
        $this->startTrans();
        try {
            // 1. 复用数据分离逻辑：分离主模型数据和关联数据
            [$mainData, $relationsData] = $this->separateData($this, $data);

            // 2. 复用字段过滤逻辑：只保留主模型有效字段（保留空值）
            $mainData = $this->filterValidModelData($this, $mainData);

            // 3. 创建主模型记录（获取自动生成的主键）
            $this->save($mainData);
            $mainModel = $this; // 保存创建后的模型实例（含主键）

            // 4. 复用关联保存逻辑：处理一对多关联（创建时无需删除现有关联，$deleteExisting设为false）
            foreach ($relationsData as $relationName => $relationItems) {
                $this->saveHasManyRelation($mainModel, $relationName, $relationItems, false);
            }

            $this->commit();
            return $mainModel; // 返回创建后的主模型实例（含主键和关联关系）

        } catch (Throwable $e) {
            $this->rollback();
            Log::error('智能创建失败: ' . $e->getMessage());
            return false;
        }
    }

    /**
     * 智能更新数据（主模型+一对多关联）
     * @param array $data 更新数据
     * @return bool 更新结果
     */
    public function intelligentUpdate(array $data): bool
    {
        // 开启事务
        $this->startTrans();
        try {


            // 4. 分离主模型数据和关联数据
            [$mainData, $relationsData] = $this->separateData($this, $data);
            // 只有当主模型数据非空时才处理更新
            if (!empty($mainData)) {
                // 过滤有效字段（保留空值）
                $mainData = $this->filterValidModelData($this, $mainData);

                // 移除主键字段（防止意外更新主键）
                $pk = $this->getPk();
                unset($mainData[$pk]);

                // 只有存在非主键字段的数据时才执行更新
                if (!empty($mainData)) {
                    $this->save($mainData);
                }
            }


            // 6. 处理一对多关联（保留空值）
            foreach ($relationsData as $relationName => $relationItems) {
                $this->saveHasManyRelation($this, $relationName, $relationItems, true);
            }

            $this->commit();
            return true;
        } catch (Throwable $e) {
            $this->rollback();
            Log::error('智能更新失败: ' . $e->getMessage());
            return false;
        }
    }

    /**
     * 过滤有效模型数据（保留空值）
     */
    protected function filterValidModelData(Model $model, array $data): array
    {
        // 获取所有有效字段（主表字段和JSON字段）
        $validFields = $model->getTableFields();

        // 移除主键（防止意外更新）
        $pk = $model->getPk();
        unset($data[$pk]);

        // 只保留有效字段，包括值为空的
        return array_intersect_key($data, array_flip($validFields));
    }

    /**
     * 分离主模型数据和关联数据
     */
    protected function separateData(Model $model, array $data): array
    {
        $mainData = [];
        $relationsData = [];

        // 获取所有有效字段
        $modelFields = $model->getTableFields();

        foreach ($data as $key => $value) {
            if (in_array($key, $modelFields)) {
                // 主模型字段（保留空值）
                $mainData[$key] = $value;
            } elseif (is_array($value)) {
                // 关联数据（保留空值）
                $relationsData[$key] = $value;
            }
        }

        return [$mainData, $relationsData];
    }


    /**
     * 保存一对多关联（支持替换/增量更新，保留空值）
     * @param Model $model 主模型实例
     * @param string $relationName 关联方法名（如：'adminRoles'）
     * @param array $relationItems 关联数据列表
     * @param bool $deleteExisting 是否先删除已有的关联数据（true=完全替换，false=增量更新）
     */
    protected function saveHasManyRelation(Model $model, string $relationName, array $relationItems, bool $deleteExisting = false): void
    {
        // 1. 验证关系类型
        if (!method_exists($model, $relationName)) {
            Log::warning("尝试更新未定义的关联: $relationName");
            return;
        }

        $relation = $model->$relationName();
        if (!$relation instanceof HasMany) {
            Log::warning("尝试更新非HasMany关系: {$relationName}（仅支持一对多关联）");
            return;
        }

        // 2. 如需完全替换关联数据，先删除已有的关联
        if ($deleteExisting) {
            // 删除当前主模型下的所有关联数据（基于外键）
            try {
                $relation->delete();
            } catch (DbException $e) {
                Log::warning("删除关联发生异常" . $e->getMessage());
                return;
            }
        }

        // 3. 准备有效数据（保留空值，过滤无效字段）
        $relatedModel = $relation->getModel();
        $relatedFields = $relatedModel->getTableFields();
        $validItems = [];

        foreach ($relationItems as $item) {
            $validItem = [];
            foreach ($item as $key => $value) {
                // 只保留关联模型的有效字段（包括空值）
                if (in_array($key, $relatedFields)) {
                    $validItem[$key] = $value;
                }
            }
            $validItems[] = $validItem;
        }

        // 4. 批量保存关联数据（更新/新增）
        if (!empty($validItems)) {
            $model->$relationName()->saveAll($validItems);
        }
    }

    /**
     * 批量删除数据及其关联的HasMany关系数据（优化模型版）
     *
     * @param array $ids 要删除的主键ID数组
     * @return bool|string 成功返回true，失败返回错误信息
     */
    public function batchDeleteWithRelation(array $ids, array $relationList = []): bool|string
    {
        if (empty($ids)) {
            return $this->false('请提供要删除的数据ID');
        }

        // 验证所有ID是否存在
        $existingIds = $this->where($this->getPk(), 'in', $ids)
            ->column($this->getPk());

        $nonExistingIds = array_diff($ids, $existingIds);
        if (!empty($nonExistingIds)) {
            return $this->false('以下ID不存在: ' . implode(', ', $nonExistingIds));
        }

        // 开启事务
        $this->startTrans();
        try {
            // 1. 批量删除所有关联数据
            $relations = $this->getDeletableRelations($relationList);


            foreach ($relations as $config) {
                // 使用关联模型进行批量删除
                $relationModel = $config['relationModel'];

                $relationModel->where($config['foreign_key'], 'in', $ids)
                    ->delete();
            }

            // 2. 删除主表数据
            $this->where($this->getPk(), 'in', $ids)
                ->delete();

            $this->commit();
            return true;
        } catch (Throwable $e) {
            $this->rollback();
            Log::error('批量删除失败: ' . $e->getMessage());
            return $this->false('批量删除操作失败: ' . $e->getMessage());
        }
    }

    /**
     * 获取模型的可删除关联关系配置（优化版）
     */
    protected function getDeletableRelations(array $relationList = []): array
    {
        static $cache = [];
        $className = static::class;

        // 使用缓存避免重复解析
        if (isset($cache[$className])) {
            return $cache[$className];
        }

        $relations = [];

        // 使用反射获取所有公共方法
        $reflection = new ReflectionClass($this);
        $methods = $reflection->getMethods(ReflectionMethod::IS_PUBLIC);

        foreach ($methods as $method) {
            $methodName = $method->getName();

            // 跳过魔术方法和基类方法
            if (str_starts_with($methodName, '__') || $method->class === Model::class) {
                continue;
            }
            if (!empty($relationList)) {
                if (!in_array($methodName, $relationList)) {
                    continue;
                }
            }

            try {
                // 获取关联实例
                $relation = $this->$methodName();

                // 只处理HasMany关系
                if ($relation instanceof HasMany) {
                    // 获取关联模型实例
                    $relationModel = $relation->getModel();
                    $foreignKey = $relation->getForeignKey();

                    // 构建关系配置
                    $relations[$methodName] = [
                        'relationModel' => $relationModel,
                        'foreign_key' => $foreignKey,
                    ];
                }
            } catch (Throwable $e) {
                // 忽略调用错误的关系
            }
        }

        $cache[$className] = $relations;
        return $relations;
    }

    /**
     * 获取树形结构数据
     */
    public function fetchTree($conditions = [], array $config = []): Collection
    {
        // 创建新实例避免污染当前实例
        $query = new static();
        
        // 应用条件
        if (!empty($conditions)) {
            $query->where($conditions);
        }
        
        // 应用配置
        $this->applyConfig($query, $config);
        
        return $query->select();
    }

    /**
     * 应用配置到模型实例
     */
    protected function applyConfigToModel($model, array $config): void
    {
        // 追加字段
        if (!empty($config['append'])) {
            $model->append($config['append']);
        }
        
        // 隐藏字段
        if (!empty($config['hidden'])) {
            $model->hidden($config['hidden']);
        }
        
        // 显示字段
        if (!empty($config['visible'])) {
            $model->visible($config['visible']);
        }
    }

    /**
     * 应用配置到查询实例
     */
    protected function applyConfig($query, array $config): void
    {
        // 排序
        if (!empty($config['orderBy'])) {
            $orderDir = $config['orderDir'] ?? 'desc';
            $query->order($config['orderBy'], $orderDir);
        }
        
        // 字段
        if (!empty($config['fields'])) {
            $query->field($config['fields']);
        }
        
        // 关联
        if (!empty($config['with'])) {
            $query->with($config['with']);
        }
        
        // 追加字段
        if (!empty($config['append'])) {
            $query->append($config['append']);
        }
        
        // 隐藏字段
        if (!empty($config['hidden'])) {
            $query->hidden($config['hidden']);
        }
        
        // 显示字段
        if (!empty($config['visible'])) {
            $query->visible($config['visible']);
        }
    }
}
